iT邦幫忙

2023 iThome 鐵人賽

DAY 18
0
Modern Web

從 Next.js 開始的 Functional Programming系列 第 18

D18 - 實作異步流程 (四)

  • 分享至 

  • xImage
  •  

回顧一下之前的進度,我們還在整個異步流程中的 步驟 6。今天會跟大家介紹如何使用 @effect/schemaClass ,以便更好的做未知型別的解析。

使用 Struct 加上 Data 的語法

const adminSchema = S.data(
  S.struct({
    _tag: S.literal('Administrator'),
    name: S.string,
    birth: S.Date,
  })
)

interface Adminstrator extends S.Schema.To<typeof adminSchema>

使用 Class 的語法建立 Administrator 類別

class Administrator extends S.Class<Administrator>()({
  _tag: S.literal('Administrator'),
  name: S.string,
  birth: S.Date
}) {}

我們可以把 Administrator 當作 typeinterface 使用,相當於一般 schemaTo 型別

const getBirth = (admin:Administrator)=>admin.birth //return type is Date

也可以把它當 schema 來用

const admin = pipe(
	'xxxxx',
	S.parseEither(Administrator)
)
// Either<ParseError, Administrator>

在兩種做法都能達到相同功能的前提下,私心推薦使用語法更簡潔的 Class

https://ithelp.ithome.com.tw/upload/images/20231003/20158615FTn7Urmane.png

不過使用 class 在方便的同時也有一個隱憂,就是 class 隱藏著很多內部狀態。貿然的改變它,很容易造成副作用,因此我們必須先認識 immutability (不變性),才能避免誤用。

不變性是一個非常重要且優秀的特性。

仔細觀察我們之前的範例,會發現我們都只使用 const 而不使用 let ,其實就是基於不變性的考量。我們應該盡可能的不要去變更變數,這是因為當我們對可變的物件進行操作時,有可能影響其他使用這個物件的程式,導致產生副作用

再次提醒 FP 真的很討厭副作用

https://ithelp.ithome.com.tw/upload/images/20231003/20158615BF4ItVGnsS.png

請看下面這個例子

Array.prototype.push("╮(╯_╰)╭")
const myArray = []

請問 myArray[0] 會是什麼呢? 直覺上我們會覺得是 undefined,但實際上它是 "╮(╯_╰)╭"

所以更好的做法是每次要對一個變數進行計算,就重新產生新的。這樣說可能有點抽象,請看以下範例

違反不變性
const a = [1, 2, 3] 
const b = a.push(4) 
// a: [1, 2, 3, 4]
// b: [1, 2, 3, 4]
// 雖然我們只是想建立 b,可是 a 也被改變了,這很危險 !
保持不變性
const a = [1, 2, 3] 
const b = [...a, 4]
// a: [1, 2, 3]
// b: [1, 2, 3, 4]

類似 effect 這樣完整的 FP 函式庫,也會提供好用的工具

import { ReadonlyArray as RA } from 'effect'

const a = [1, 2, 3] 
const b = RA.append(4)
// a: [1, 2, 3]
// b: [1, 2, 3, 4]
// 題外話: 這時候 b 的型別是 [number, ...[number]] ,這樣我們就可以確定它不是空的 !

以 class 來說,我們可以使用 getter

class Administrator extends S.Class<Administrator>()({
  _tag: S.literal('Administrator'),
  name: S.string,
  birth: S.Date
}) {
  get upperName() {
    return this.name.toUpperCase
  }
}

也可以使用不涉及 this 的變更的 method

class Administrator extends S.Class<Administrator>()({
  _tag: S.literal('Administrator'),
  name: S.string,
  birth: S.Date
}) {
  greeting() {
    return this.name.toUpperCase
  }
}

只要正確使用,Class 也能很 FP !


上一篇
D17 - 實作異步流程 (三)
下一篇
D19 - 實作異步流程 (五)
系列文
從 Next.js 開始的 Functional Programming30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言